-
Notifications
You must be signed in to change notification settings - Fork 37
Implement system macro make_timestamp #988
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #988 +/- ##
==========================================
+ Coverage 78.47% 78.54% +0.07%
==========================================
Files 138 138
Lines 33385 33648 +263
Branches 33385 33648 +263
==========================================
+ Hits 26198 26429 +231
- Misses 5179 5183 +4
- Partials 2008 2036 +28 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
// We have a value, if it is anything other than Day then it is invalid. | ||
if arg.0 != MakeTimestampArgs::Day { | ||
return IonResult::decoding_error(format!("value provided for '{parameter}', but no day specified.")); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How could this be reached?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By invoking make_timestamp
without a value for day, but providing a value for a parameter later:
(:make_timestamp 2025 1 (:none) 5)
Each known parameter has an entry in the expander's argument iterator, so we have to evaluate each parameter to make sure they resolve to an empty value to consider them not provided.
src/lazy/expanded/macro_evaluator.rs
Outdated
.filter(|v| *v >= 1 && *v <= 31) | ||
// Spec says 1 indexed day for the given month; does this mean we accept any integer | ||
// >=1? or does the spec expect us to validate that the day is appropriate for the | ||
// month and year? | ||
.ok_or_else(|| IonError::decoding_error("value provided for 'Day' parameter is out of range [1, 31]"))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should leave this sort of validation for Timestamp
and/or TimestampBuilder
to handle so that we don't have duplicated logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timestamp
and TimestampBuilder
currently do not do any range checking, and the spec for make_timestamp
differs from what is possible with the timestamp encoding.
Mostly around the year, where a long form timestamp can handle a year up to 2^14 (If I'm not misreading the spec), but make_timestamp
is limiting years to the range 0-9999.
The question of whether we check "max day of any month" vs, checking "is this day valid for this month and year" should also then be raised to see if we want that kind of check added to the Timestamp/TimestampBuilder.
I'd have no problem with validating in the timestamp creation, but I don't think we'd want the checks to artificially limit the dates representable by timestamp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec limits the year range to be 0..=9999
for Timestamp
(indirectly by mentioning that it follows the W3C note on date and time formats) , and so Timestamp
should only be able to represent valid points in time in that range (but forgetting about leap seconds and that sort of nonsense).
Timestamp
and/or TimestampBuilder
should have range checking, even if it is done indirectly somehow. I would recommend omitting the checks here, and creating an issue to go back and make sure that the TimestampBuilder
cannot be used to construct an invalid Timestamp
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good point. Yea, I like that. I'll remove the range checks and plan to add them to timestamp or timestamp builder asap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realized there is range checking in the Timestamp, on build()
, but it seems to be done through chrono
.
Removing the range checking in the macro impl does lead to odd error behaviors in some cases. For instance, most arguments to the macro come in as Int
, and when creating a timestamp they need to be converted down to a u32
. So when converting I'll have to emit errors for not being able to convert to a u32
, without really checking the range and providing a more context-aware error.
Doesn't seem like a big deal, just a bit weird.
src/lazy/expanded/macro_evaluator.rs
Outdated
// Correct with the exponent jic the value is normalized to 5x10^1 or something. | ||
let whole_seconds_i64 = whole_seconds_i64 * 10i64.pow(whole_seconds.exponent() as u32); | ||
|
||
let builder = builder.clone().with_second(whole_seconds_i64 as u32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to clone()
the builder here? If so, can you add a note as to why?
src/lazy/expanded/macro_evaluator.rs
Outdated
|
||
// Check if we have a value. | ||
let Some(value_ref) = arg.try_get_valueref(env)? else { | ||
return Ok(self.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why clone()
the builder-wrapper? Could we just make this function take self
instead of &self
so that it can return the original? Or perhaps &mut self
so that it could swap out the value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I made it a &self
, because I wasn't sure how the process functions would end up being compiled, and kept the move-oriented API of the underlying builder. My understanding is that a non-ref will result in a memcpy when crossing function boundaries (if not inlined).
Going the &mut self
route might be more straight forward. Since the type isn't changing, there's no reason to invalidate the pre-process value.
Will change that in a new commit, ty!
Issue #, if available: #942 (and a little of #970)
Description of changes:
This PR implements the
make_timestamp
system macro. It's a bigger PR than I really wanted, but I also wanted something to handle the timestamp builder a little better.There are a few changes in here that are not directly related to
make_timestamp
, but do get used in the implementation:Sub
forDecimal
. I originally added this thinking some arithmetic operations on Decimals would make handling the fractional second Decimal nicer. Ultimately I didn't use it.fract
andtrunc
to Decimal to handle extracting the whole and fractional values from the decimal. This mirrors the floating point types in Rust.SystemMacroArguments
type to be a little more generic. It takes a generic argument that isAsRef<str>
, which lets you provide a type that can be used to return a&str
for the parameter name. In themake_timestamp
implementation this is used to provide an enum that can be used to provide both the argument's position, as well as the name. Right now the only 2 implementations that are using it aremake_decimal
andmake_timestamp
, so it seems a bit overkill, but I'll circle back around and revisit the other system macros where it could be used.The
TimestampBuilderWrapper
type that I'm using to handle the changingTimestampBuilder
types adds a lot of bulk to the diff and I'm not too thrilled about that.Conformance Test
Conformance tests for
make_timestamp
are passing:CLI
I have a local edit of the ion-cli to work with ion-rust HEAD. So I've been able to test the newer system macros that weren't part of rc6:
This has been super helpful.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.